package com.cgwx.commonutils.math;

import com.cgwx.commonutils.struc.Bag;

/**
 * @author GaoWei
 * @Date 2021/8/6 - 10:42
 */
public abstract class Expression
{
    protected Expression parent = null;

    /**
     * 求值
     * @param paramVarMap 参数变量Map
     * @param paramFuncMap 参数函数Map
     * @return 求值结果
     */
    public abstract double eval(VarMap paramVarMap, FuncMap paramFuncMap);

    /**
     * 是否是后代节点
     * @param x 待判断
     * @return 判断结果
     */
    public boolean isDescendent(Expression x) {
        Expression y = this;
        while (y != null) {
            if (y == x) {
                return true;
            }
            y = y.parent;
        }
        return false;
    }

    public Expression getParent() {
        return this.parent;
    }

    protected void checkBeforeAccept(Expression x) {
        if (x == null) {
            throw new IllegalArgumentException("expression cannot be null");
        }
        if (x.parent != null) {
            throw new IllegalArgumentException("expression must be removed parent");
        }
        if (isDescendent(x)) {
//            循环应用
            throw new IllegalArgumentException("cyclic reference");
        }
    }

    public String[] getVariableNames() {
        return getTermNames(true);
    }

    public String[] getFunctionNames() {
        return getTermNames(false);
    }

    private String[] getTermNames(boolean varNames) {
        Bag b = new Bag();
        getTermNames(this, b, varNames);
        String[] arr = new String[b.size()];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = ((String)b.get(i));
        }
        return arr;
    }

    private static void getTermNames(Expression x, Bag b, boolean varNames) {
        if ((x instanceof OpNode)) {
            OpNode o = (OpNode)x;
            getTermNames(o.leftChild, b, varNames);
            getTermNames(o.rightChild, b, varNames);
        }
        else if ((x instanceof VarNode)) {
            if (varNames) {
                VarNode v = (VarNode)x;
                if (!b.contains(v.name)) {
                    b.add(v.name);
                }
            }
        }
        else if ((x instanceof FuncNode)) {
            FuncNode f = (FuncNode)x;
            if ((!varNames) &&
                    (!b.contains(f.name))) {
                b.add(f.name);
            }
            for (int i = 0; i < f.numChildren(); i++) {
                getTermNames(f.child(i), b, varNames);
            }
        }
    }

    public String toString() {
        StringBuffer sb = new StringBuffer();
        toString(this, sb);
        return sb.toString();
    }

    private static void toString(Expression x, StringBuffer sb) {
        if ((x instanceof OpNode)) {
            OpNode o = (OpNode)x;
            sb.append("(");
            toString(o.leftChild, sb);
            sb.append(o.getSymbol());
            toString(o.rightChild, sb);
            sb.append(")");
        }
        else if ((x instanceof TermNode)) {
            TermNode t = (TermNode)x;
            if (t.getNegate()) {
                sb.append("(");
                sb.append("-");
            }
            sb.append(t.getName());
            if ((t instanceof FuncNode)) {
                FuncNode f = (FuncNode)t;

                sb.append("(");
                if (f.numChildren() > 0) {
                    toString(f.child(0), sb);
                }
                for (int i = 1; i < f.numChildren(); i++) {
                    sb.append(", ");
                    toString(f.child(i), sb);
                }
                sb.append(")");
            }
            if (t.getNegate()) {
                sb.append(")");
            }
        }
        else if ((x instanceof ValNode)) {
            sb.append(((ValNode)x).val);
        }
    }
}
